1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.hiprenderer.backend.d3d.d3drenderer;
12 version(Windows):
13 version(DirectX):
14 
15 pragma(lib, "ole32");
16 pragma(lib, "d3dcompiler");
17 // pragma(lib, "d3dcompiler_47");
18 pragma(lib, "d3d11");
19 pragma(lib, "dxgi");
20 
21 import core.stdc.string;
22 import hip.util.string:fromStringz;
23 
24 import directx.d3d11;
25 import directx.d3d11_3;
26 import directx.dxgi1_4;
27 
28 
29 import hip.util.system;
30 import hip.config.opts;
31 import hip.error.handler;
32 
33 import hip.hiprenderer.shader;
34 import hip.hiprenderer.viewport;
35 import hip.hiprenderer.renderer;
36 import hip.hiprenderer.framebuffer;
37 
38 import hip.hiprenderer.backend.d3d.d3dshader;
39 import hip.hiprenderer.backend.d3d.d3dframebuffer;
40 import hip.hiprenderer.backend.d3d.d3dvertex;
41 import hip.hiprenderer.backend.d3d.d3dtexture;
42 
43 
44 version(UWP)
45 {
46     import hip.bind.external : getCoreWindow, HipExternalCoreWindow;
47 }
48 else version = WindowsNative;
49 
50 ID3D11Device3 _hip_d3d_device = null;
51 ID3D11DeviceContext _hip_d3d_context = null;
52 ID3D11DeviceContext3 _hip_d3d_context3 = null;
53 IDXGISwapChain3 _hip_d3d_swapChain = null;
54 ID3D11RenderTargetView _hip_d3d_mainRenderTarget = null;
55 private __gshared bool errorCheckEnabled = true;
56 
57 
58 void d3dCall(HRESULT delegate() dg, string file = __FILE__, size_t line = __LINE__)
59 {
60     auto res = dg();
61     if(FAILED(res))
62     {
63         // getWindowsErrorMessage(hr);
64         HipRenderer.exitOnError(file, line);
65     }
66 }
67 
68 class Hip_D3D11_Renderer : IHipRendererImpl
69 {
70     import hip.windowing.window;
71     public static HipWindow window = null;
72     protected static bool hasDebugAvailable;
73     package static D3D11_BLEND_DESC blend;
74     protected static ID3D11BlendState blendState;
75     protected static Viewport currentViewport;
76     public static Shader currentShader;
77 
78     static if(HIP_DEBUG)
79     {
80         import directx.dxgidebug;
81         IDXGIInfoQueue dxgiQueue;
82     }
83     
84 
85     static void assertExit(HRESULT hres, string msg,
86     string file = __FILE__, size_t line = __LINE__,
87     string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
88     {
89         ErrorHandler.assertLazyExit(SUCCEEDED(hres), msg~":\n\t"~getWindowsErrorMessage(hres),file,line,mod,func);
90     }
91 
92     protected void createD3DDevice()
93     {
94         uint createDeviceFlags = 0;
95         static if(HIP_DEBUG){
96             pragma(msg, "D3D11_CREATE_DEVICE_DEBUG:\n\tComment this flag if you do not have d3d11 debug device installed");
97             /**
98             * https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer
99             *
100             * For Windows 10, to create a device that supports the debug layer,
101             * enable the "Graphics Tools" optional feature. Go to the Settings panel,
102             * under System, Apps & features, Manage optional Features,
103             * Add a feature, and then look for "Graphics Tools".
104             */
105             createDeviceFlags|= D3D11_CREATE_DEVICE_DEBUG;
106         }
107         const D3D_FEATURE_LEVEL[] levelArray = 
108         [
109             D3D_FEATURE_LEVEL_12_1,
110             D3D_FEATURE_LEVEL_12_0,
111             D3D_FEATURE_LEVEL_11_1,
112             D3D_FEATURE_LEVEL_11_0,
113             D3D_FEATURE_LEVEL_10_1,
114             D3D_FEATURE_LEVEL_10_0,
115             D3D_FEATURE_LEVEL_9_3,
116             D3D_FEATURE_LEVEL_9_1
117         ];
118         D3D_FEATURE_LEVEL featureLevel;
119 
120         ID3D11Device device;
121         ID3D11DeviceContext context;
122         HRESULT hres = D3D11CreateDevice(cast(IDXGIAdapter)null,
123             D3D_DRIVER_TYPE_HARDWARE,
124             null, createDeviceFlags,
125             levelArray.ptr,
126             cast(uint)levelArray.length,
127             D3D11_SDK_VERSION,
128             &device,
129             &featureLevel,
130             &context
131         );
132 
133         if (FAILED(hres))
134         {
135             ErrorHandler.showWarningMessage("D3D11: Hardware rendering device creation failed",
136             "HipRenderer will try to use software renderer");
137             // Initialization failed, fall back to the WARP device.
138             hres = D3D11CreateDevice(
139                 cast(IDXGIAdapter)null,
140                 D3D_DRIVER_TYPE_WARP,
141                 null,
142                 createDeviceFlags,
143                 levelArray.ptr,
144                 cast(uint)levelArray.length,
145                 D3D11_SDK_VERSION,
146                 &device,
147                 &featureLevel,
148                 &context);
149             assertExit(SUCCEEDED(hres), "D3D11 Could not creating any rendering context: ");
150         }
151 
152         device.QueryInterface(&IID_ID3D11Device3, cast(void**)&_hip_d3d_device);
153         _hip_d3d_context = context;
154     }
155 
156     version(UWP)
157     protected void createD3DSwapChainForCoreWindow(HipExternalCoreWindow wnd,  HipRendererConfig* config)
158     {
159         DXGI_SWAP_CHAIN_DESC1 dsc;
160         import core.stdc.string;
161         memset(&dsc, 0, DXGI_SWAP_CHAIN_DESC1.sizeof);
162         dsc.Width = wnd.logicalWidth;
163         dsc.Height = wnd.logicalHeight;
164         dsc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
165         dsc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
166         dsc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
167 
168         ubyte bufferCount = 2;
169         ubyte samplingLevel = 1;
170         if(config != null)
171             bufferCount = config.bufferingCount;
172         if(config != null && config.multisamplingLevel > 0)
173             samplingLevel = config.multisamplingLevel;
174 
175         dsc.BufferCount = bufferCount;
176         dsc.SampleDesc.Count = samplingLevel;
177         dsc.SampleDesc.Quality = 0;
178         // dsc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
179         dsc.Scaling = DXGI_SCALING_ASPECT_RATIO_STRETCH;
180         dsc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
181 
182 
183         IDXGIDevice3 dxgiDevice;
184         assertExit(
185             _hip_d3d_device.QueryInterface(&IID_IDXGIDevice3, cast(void**)&dxgiDevice)
186         ,"Could not get the IDXGIDevice3 interface");
187 
188         IDXGIAdapter adapter;
189         assertExit(
190             dxgiDevice.GetAdapter(&adapter)
191         , "Could not get DXGI Adapter");
192 
193         IDXGIFactory4 factory;
194         assertExit(
195             adapter.GetParent(&IID_IDXGIFactory4, cast(void**)&factory)
196         ,"Could not get IDXGIFactory4");
197         
198         IDXGISwapChain1 swapChain;
199         assertExit(factory.CreateSwapChainForCoreWindow (
200             cast(IUnknown)_hip_d3d_device,
201             cast(IUnknown)wnd.coreWindow,
202             &dsc,
203             cast(IDXGIOutput)null,
204             &swapChain
205         ), "Could not create swap chain for CoreWindow");
206 
207         swapChain.QueryInterface(&IID_IDXGISwapChain3, cast(void**)&_hip_d3d_swapChain);
208     }
209     
210     protected void initD3DDebug()
211     {
212         static if(HIP_DEBUG)
213         {
214             import hip.util.windows;
215             HRESULT hres;
216             DXGIGetDebugInterface = cast(_DXGIGetDebugInterface)GetProcAddress(GetModuleHandle("Dxgidebug.dll"), "DXGIGetDebugInterface");
217             if(DXGIGetDebugInterface is null)
218             {
219                 ErrorHandler.showErrorMessage("DLL Error", "Error loading the DXGIGetDebugInterface from Dxgidebug.dll
220                 Debug layer will be aborted.");
221                 return;
222             }
223             hres = DXGIGetDebugInterface(&uuidof!IDXGIInfoQueue, cast(void**)&dxgiQueue);
224             if(FAILED(hres))
225             {
226                 ErrorHandler.showErrorMessage("DXGI Error", "Could not get the IDXGIInfoQueue interface. \nError: " ~
227                 getWindowsErrorMessage(hres) ~ "\nDebug layer will be aborted.");
228             }
229             else
230                 hasDebugAvailable = true;
231         }
232     }
233 
234     protected void initD3DRenderTargetView()
235     {
236         HRESULT res;
237         ID3D11Texture2D pBackBuffer;
238 
239         res = _hip_d3d_swapChain.GetBuffer(0, &IID_ID3D11Texture2D, cast(void**)&pBackBuffer);
240         ErrorHandler.assertLazyErrorMessage(SUCCEEDED(res), "Error creating D3D11Texture2D", getWindowsErrorMessage(res));
241 
242         //Use back buffer address to create a render target
243         res = _hip_d3d_device.CreateRenderTargetView(pBackBuffer, null, &_hip_d3d_mainRenderTarget);
244         ErrorHandler.assertLazyErrorMessage(SUCCEEDED(res), "Error creating render target view", getWindowsErrorMessage(res));
245         pBackBuffer.Release();
246 
247         _hip_d3d_context.OMSetRenderTargets(1u, &_hip_d3d_mainRenderTarget, null);
248         setState();
249     }
250     protected void initD3DFowHWND(HWND hwnd, HipRendererConfig* config)
251     {
252         DXGI_SWAP_CHAIN_DESC dsc;
253         dsc.OutputWindow = hwnd;
254         dsc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
255         dsc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
256         dsc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
257 
258         ubyte bufferCount = 2;
259         ubyte samplingLevel = 1;
260         if(config != null)
261             bufferCount = config.bufferingCount;
262         if(config != null && config.multisamplingLevel > 0)
263             samplingLevel = config.multisamplingLevel;
264 
265         dsc.BufferCount = bufferCount;
266         dsc.SampleDesc.Count = samplingLevel;
267         dsc.SampleDesc.Quality = 0;
268         dsc.Windowed = TRUE; //True
269         //Let user being able to switch between fullscreen and windowed
270         dsc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
271 
272         // dsc.BufferDesc.RefreshRate.Numerator = 60;
273         // dsc.BufferDesc.RefreshRate.Denominator = 1;
274 
275         uint createDeviceFlags = 0;
276         static if(HIP_DEBUG)
277             createDeviceFlags|= D3D11_CREATE_DEVICE_DEBUG;
278         const D3D_FEATURE_LEVEL[] levelArray = 
279         [
280             D3D_FEATURE_LEVEL_11_1,
281             D3D_FEATURE_LEVEL_11_0,
282             D3D_FEATURE_LEVEL_10_1,
283             D3D_FEATURE_LEVEL_10_0,
284             D3D_FEATURE_LEVEL_9_3,
285             D3D_FEATURE_LEVEL_9_1
286         ];
287         D3D_FEATURE_LEVEL featureLevel;
288 
289         ID3D11Device device;
290         IDXGISwapChain swapChain;
291         auto res = D3D11CreateDeviceAndSwapChain(null,
292                                                 D3D_DRIVER_TYPE_HARDWARE,
293                                                 null,
294                                                 createDeviceFlags,
295                                                 levelArray.ptr,
296                                                 cast(uint)levelArray.length,
297                                                 D3D11_SDK_VERSION,
298                                                 &dsc,
299                                                 &swapChain,
300                                                 &device,
301                                                 &featureLevel,
302                                                 &_hip_d3d_context);
303 
304         
305 
306         swapChain.QueryInterface(&IID_IDXGISwapChain3, cast(void**)&_hip_d3d_swapChain);
307         device.QueryInterface(&IID_ID3D11Device3, cast(void**)&_hip_d3d_device);
308 
309 
310         if(ErrorHandler.assertLazyErrorMessage(SUCCEEDED(res), "D3D11: Error creating device and swap chain", getWindowsErrorMessage(res)))
311         {
312             Hip_D3D11_Dispose();
313             return;
314         }
315 
316         initD3DDebug();
317         initD3DRenderTargetView();
318     }
319 
320     version(UWP)
321     protected void initD3DForCoreWindow(HipExternalCoreWindow wnd, HipRendererConfig* config)
322     {
323         createD3DDevice();
324         initD3DDebug();
325         createD3DSwapChainForCoreWindow(wnd, config);
326         initD3DRenderTargetView();
327     }
328 
329     /**
330     *   Create a rasterizer state
331     */
332     public void setState()
333     {
334         D3D11_RASTERIZER_DESC desc;
335         desc.CullMode = D3D11_CULL_NONE;
336         ID3D11RasterizerState state;
337 
338         if(FAILED(_hip_d3d_device.CreateRasterizerState(&desc, &state)))
339             HipRenderer.exitOnError();
340 
341         _hip_d3d_context.RSSetState(state);
342     }
343     public final bool isRowMajor(){return false;}
344     void setErrorCheckingEnabled(bool enable = true)
345     {
346         errorCheckEnabled = enable;
347     }
348 
349     public bool hasErrorOccurred(out string err, string file = __FILE__, size_t line = __LINE__)
350     {
351         if(hasDebugAvailable)
352         {
353             DXGI_INFO_QUEUE_MESSAGE* msg;
354             bool hasError;
355             size_t msgSize;
356             for(ulong i = 0, 
357             // len = dxgiQueue.GetNumStoredMessagesAllowedByRetrievalFilters(DXGI_DEBUG_DX);
358             len = dxgiQueue.GetNumStoredMessages(DXGI_DEBUG_DX);
359             i < len; i++)
360             {
361                 import core.stdc.stdlib;
362                 dxgiQueue.GetMessage(DXGI_DEBUG_DX, i, null, &msgSize);
363                 msg = cast(DXGI_INFO_QUEUE_MESSAGE*)malloc(msgSize);
364                 dxgiQueue.GetMessage(DXGI_DEBUG_DX, i, msg, &msgSize);
365                 if(msg.pDescription !is null || msg.Severity == DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR)
366                 {
367                     hasError = true;
368                     err~= msg.pDescription.fromStringz~"\n";
369                 }
370                 free(msg);
371             }
372             return hasError;
373         }
374         else
375         {
376             HRESULT hres = GetLastError();
377             err = getWindowsErrorMessage(hres);
378             return FAILED(hres);
379         }
380     }
381 
382     public bool setWindowMode(HipWindowMode mode)
383     {
384         final switch(mode) with(HipWindowMode)
385         {
386             case BORDERLESS_FULLSCREEN:
387                 break;
388             case FULLSCREEN:
389                 break;
390             case WINDOWED:
391 
392                 break;
393         }
394         return false;
395     }
396 
397     public IHipFrameBuffer createFrameBuffer(int width, int height)
398     {
399         return new Hip_D3D11_FrameBuffer(width,height);
400     }
401     public IHipVertexArrayImpl  createVertexArray()
402     {
403         return new Hip_D3D11_VertexArrayObject();
404     }
405     public IHipTexture createTexture()
406     {
407         return new Hip_D3D11_Texture();
408     }
409     public IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage)
410     {
411         return new Hip_D3D11_VertexBufferObject(size, usage);
412     }
413     public IHipIndexBufferImpl  createIndexBuffer(index_t count, HipBufferUsage usage)
414     {
415         return new Hip_D3D11_IndexBufferObject(count, usage);
416     }
417 
418     public Shader createShader()
419     {
420         return new Shader(new Hip_D3D11_ShaderImpl());
421     }
422 
423     bool isBlendingEnabled() const
424     {
425         version(none)ErrorHandler.assertExit(false, "Unimplemented");
426         return false;
427     }
428 
429     public bool init(HipWindow window)
430     {
431         version(UWP){assert(false, "Cannot call 'init' on UWP. Use initExternal");}
432         else
433         {
434             this.window = window;
435 
436             HipRendererConfig cfg = HipRenderer.getCurrentConfig();
437             initD3DFowHWND(window.hwnd, &cfg);
438             HipRenderer.rendererType = HipRendererType.D3D11;
439 
440             return ErrorHandler.stopListeningForErrors();
441         }
442     }
443 
444     version(dll)
445     {
446         public bool initExternal()
447         {
448             HipRendererConfig cfg;
449             initD3DForCoreWindow(getCoreWindow(), &cfg);
450             return true;
451         }
452     }
453 
454     public int queryMaxSupportedPixelShaderTextures()
455     {
456         return D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
457     }
458 
459     private int getRendererMode(HipRendererMode mode)
460     {
461         final switch(mode) with (HipRendererMode)
462         {
463             case TRIANGLES:
464                 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
465             case TRIANGLE_STRIP:
466                 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
467             case LINE:
468                 return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
469             case LINE_STRIP:
470                 return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
471             case POINT:
472                 return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
473         }
474     }
475 
476     public void setRendererMode(HipRendererMode mode)
477     {
478         _hip_d3d_context.IASetPrimitiveTopology(getRendererMode(mode));
479     }
480 
481     public void setViewport(Viewport v)
482     {
483         import hip.windowing.platforms.windows;
484         version(WindowsNative)
485             int[2] borders = getWindowBorder(window.hwnd);
486         else
487             int[2] borders = [0,0];
488 
489         D3D11_VIEWPORT vp;
490         memset(&vp, 0, D3D11_VIEWPORT.sizeof);
491         vp.Width = v.width - borders[0];
492         vp.Height = v.height - borders[1];
493         vp.TopLeftX = v.x;
494         vp.TopLeftY = v.y;
495         // vp.MinDepth = 0;
496         // vp.MaxDepth = 1;
497 
498 
499         currentViewport = v;
500         _hip_d3d_context.RSSetViewports(1u, &vp);
501     }
502     
503 
504 
505     void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255){}
506     void setShader(Shader s)
507     {
508         currentShader = s;
509     }
510 
511     void begin()
512     {
513         // if(HipRenderer.currentShader != currentShader)
514         //     HipRenderer.setShader(currentShader);
515         _hip_d3d_context.OMSetRenderTargets(1u, &_hip_d3d_mainRenderTarget, null);
516     }
517     void end()
518     {
519         _hip_d3d_swapChain.Present(0,0);
520     }
521 
522     public void drawVertices(index_t count, uint offset = 0)
523     {
524         _hip_d3d_context.Draw(count, offset);
525     }
526     public void drawIndexed(index_t indicesSize, uint offset=0)
527     {
528         _hip_d3d_context.DrawIndexed(indicesSize, offset, 0);
529     }
530     void clear(){}
531     
532     void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
533     {
534         float[4] color = [cast(float)r/255, cast(float)g/255, cast(float)b/255, cast(float)a/255];
535         _hip_d3d_context.ClearRenderTargetView(_hip_d3d_mainRenderTarget, color.ptr);
536     }
537 
538     public void dispose()
539     {
540         Hip_D3D11_Dispose();
541     }
542     
543     public void setDepthTestingFunction(HipDepthTestingFunction)
544     {
545         
546     }
547     public void setDepthTestingEnabled(bool)
548     {
549         
550     }
551     public void setStencilTestingEnabled(bool bEnable)
552     {
553     }
554 
555     public void setStencilTestingMask(uint mask)
556     {
557     }
558 
559     public void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a)
560     {
561         
562     }
563 
564     public void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask)
565     {
566     }
567 
568     public void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass)
569     {
570     }
571     
572     public ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length)
573     {
574         return null;
575     }
576     
577     
578 }
579 
580 private void Hip_D3D11_Dispose()
581 {
582     if(_hip_d3d_swapChain)
583     {
584         _hip_d3d_swapChain.SetFullscreenState(FALSE, null);
585         _hip_d3d_swapChain.Release();
586         _hip_d3d_swapChain = null;
587     }
588     if(_hip_d3d_context)
589     {
590         _hip_d3d_context.Release();
591         _hip_d3d_context = null;
592     }
593     if(_hip_d3d_device)
594     {
595         _hip_d3d_device.Release();
596         _hip_d3d_device = null;
597     }
598     if(_hip_d3d_mainRenderTarget)
599     {
600         _hip_d3d_mainRenderTarget.Release();
601         _hip_d3d_mainRenderTarget = null;
602     }
603 }
604